package genetic;

import functionTypes.ConstantSampling;
import functionTypes.Interpolation;
import functionTypes.SamplingFunction;
import functionTypes.OldFunction;
import tasks.Boiler;
import tasks.BoilerControlSystem;
import Abstracts.AbstractControlSystem;

public class Island {

	public AbstractControlSystem[][] field;
	public double[][] scores;
	public String task;
	public String type;
	public int islandsCount = 5;
	public int onIsland = 5;
	public int maxEvolutionCount = 100;
	public double mutationProbability = 0.05;

	public double topScores[];

	public AbstractControlSystem bestIndividual;
	public double bestIndividualScore;

	public Boiler boiler;

	private int left(int a) {
		if (a == 0)
			return islandsCount - 1;
		return a;
	}

	private int right(int a) {
		if (a == islandsCount - 1)
			return 0;
		return a;
	}

	public Island(String task, String type, int islandsCount, int onIsland,
			int maxEvolutionCount, double mutationProbability) throws Exception {
		scores = new double[islandsCount][onIsland];

		if (maxEvolutionCount > 2) {
			this.maxEvolutionCount = maxEvolutionCount;
		}
		topScores = new double[maxEvolutionCount];

		if ((mutationProbability > 0) && (mutationProbability < 1)) {
			this.mutationProbability = mutationProbability;
		}

		if (!(type.equals("old") || type.equals("sampling")
				|| type.equals("constantSampling") || type
				.equals("interpolation"))) {
			throw new Exception(
					"Types may be: old, sampling, constantSampling, interpolation");
		}

		if (task.equals("boiler")) {
			this.task = "boiler";
			this.type = type;
			if (islandsCount > 2) {
				this.islandsCount = islandsCount;
			}
			boiler = new Boiler();
			createBoilerField();
			if (type.equals("old")) {
				oldBoilerGenetic();
			} else if (type.equals("sampling")) {
				samplingBoilerGenetic();
			} else if (type.equals("constantSampling")) {
				constatntSamplingBoilerGenetic();
			} else if (type.equals("interpolation")) {
				interpolationBoilerGenetic();
			}
		}
	}

	public void createBoilerField() {
		field = new BoilerControlSystem[islandsCount][onIsland];

		int innerType = 0;

		if (type.equals("old")) {
			innerType = 0;
		} else if (type.equals("sampling")) {
			innerType = 1;
		} else if (type.equals("constantSampling")) {
			innerType = 2;
		} else if (type.equals("interpolation")) {
			innerType = 3;
		}

		for (int i = 0; i != islandsCount; ++i) {
			for (int j = 0; j != onIsland; ++j) {
				field[i][j] = new BoilerControlSystem();
				if (innerType == 0) {
					OldFunction[] functions = new OldFunction[field[i][j]
							.getDimensions()];
					for (int k = 0; k != functions.length; ++k) {
						functions[k] = new OldFunction();
						functions[k].generateFunction();
					}
					field[i][j].setControlSystem(functions);
				} else if (innerType == 1) {
					SamplingFunction[] functions = new SamplingFunction[field[i][j]
							.getDimensions()];
					for (int k = 0; k != functions.length; ++k) {
						functions[k] = new SamplingFunction();
						functions[k].generateFunction();
					}
					field[i][j].setControlSystem(functions);
				} else if (innerType == 2) {
					ConstantSampling[] functions = new ConstantSampling[field[i][j]
							.getDimensions()];
					for (int k = 0; k != functions.length; ++k) {
						functions[k] = new ConstantSampling();
						functions[k].generateFunction();
					}
					field[i][j].setControlSystem(functions);
				} else if (innerType == 3) {
					Interpolation[] functions = new Interpolation[field[i][j]
							.getDimensions()];
					for (int k = 0; k != functions.length; ++k) {
						functions[k] = new Interpolation();
						functions[k].generateFunction();
					}
					field[i][j].setControlSystem(functions);
				}
			}
		}
	}

	public void oldBoilerGenetic() {
		bestIndividualScore = Double.POSITIVE_INFINITY;

		for (int i = 0; i < maxEvolutionCount; ++i) {
			System.out.println("\nEvolution step:" + i);
			for (int j = 0; j < islandsCount; ++j) {
				for (int k = 0; k < onIsland; ++k) {

					if (mutationProbability > Math.random()) {
						field[j][k].generateControlSystem();
						continue;
					}

					int a = (int) (Math.random() * onIsland);
					int b = a;
					while (b == a) {
						b = (int) (Math.random() * onIsland);
					}

					OldFunction[] oldF1 = (OldFunction[]) field[j][a]
							.getFunctions();
					OldFunction[] oldF2 = (OldFunction[]) field[j][b]
							.getFunctions();

					OldFunction[] newF1 = new OldFunction[oldF1.length];
					OldFunction[] newF2 = new OldFunction[oldF1.length];

					for (int fffuuu = 0; fffuuu < oldF1.length; ++fffuuu) {
						OldFunction[] crossResult = oldF1[fffuuu]
								.crossover(oldF2[fffuuu]);
						newF1[fffuuu] = crossResult[2];
						newF2[fffuuu] = crossResult[3];
					}

					BoilerControlSystem cs1 = new BoilerControlSystem(oldF1);
					BoilerControlSystem cs2 = new BoilerControlSystem(oldF2);
					BoilerControlSystem cs3 = new BoilerControlSystem(newF1);
					BoilerControlSystem cs4 = new BoilerControlSystem(newF2);

					BoilerControlSystem tmpCS1 = cs1;
					BoilerControlSystem tmpCS2 = cs2;

					double eff1 = boiler.getEffectivnes(cs1);
					double eff2 = boiler.getEffectivnes(cs2);
					double eff3 = boiler.getEffectivnes(cs3);
					double eff4 = boiler.getEffectivnes(cs4);

					if (eff3 < eff1) {
						tmpCS1 = cs3;
						eff1 = eff3;
					} else if (eff3 < eff2) {
						tmpCS2 = cs3;
						eff2 = eff3;
					}

					if (eff1 < eff2) {
						if (eff4 < eff2) {
							tmpCS2 = cs4;
							eff2 = eff4;
						}
					} else {
						if (eff4 < eff1) {
							tmpCS1 = cs4;
							eff1 = eff4;
						}
					}

					field[j][a] = cs1;
					field[j][b] = cs2;

					if (eff1 < eff2) {
						if (eff1 < bestIndividualScore) {
							bestIndividualScore = eff1;
							bestIndividual = cs1;
						}
					} else {
						if (eff2 < bestIndividualScore) {
							bestIndividualScore = eff2;
							bestIndividual = cs2;
						}
					}
				}
			}
			if (((int) (i / 10)) * 10 == i) {
				int qwe = islandsCount * onIsland / 2;
				for (int k = 0; k != qwe; ++k) {
					int a = (int) (Math.random() * islandsCount);
					int b = a;
					while (a != b) {
						b = (int) (Math.random() * islandsCount);
					}
					int c = (int) (Math.random() * onIsland);
					int d = (int) (Math.random() * onIsland);

					AbstractControlSystem tmp = field[a][c];
					field[a][c] = field[b][d];
					field[b][d] = tmp;
				}
			}
			topScores[i] = bestIndividualScore;
			System.out.println("\n\n///////////////////////////");
			System.out.println("step " + i);
			System.out.println("best " + bestIndividualScore);
		}
	}

	public void samplingBoilerGenetic() throws Exception {
		bestIndividualScore = Double.POSITIVE_INFINITY;

		for (int i = 0; i < maxEvolutionCount; ++i) {
			System.out.println("\nEvolution step:" + i);
			for (int j = 0; j < islandsCount; ++j) {
				for (int k = 0; k < onIsland; ++k) {

					if (mutationProbability > Math.random()) {
						field[j][k].generateControlSystem();
						continue;
					}

					int a = (int) (Math.random() * onIsland);
					int b = a;
					while (b == a) {
						b = (int) (Math.random() * onIsland);
					}

					SamplingFunction[] oldF1 = (SamplingFunction[]) field[j][a]
							.getFunctions();
					SamplingFunction[] oldF2 = (SamplingFunction[]) field[j][b]
							.getFunctions();

					SamplingFunction[] newF1 = new SamplingFunction[oldF1.length];
					SamplingFunction[] newF2 = new SamplingFunction[oldF1.length];

					for (int fffuuu = 0; fffuuu < oldF1.length; ++fffuuu) {
						SamplingFunction[] crossResult = oldF1[fffuuu]
								.crossover(oldF2[fffuuu]);
						newF1[fffuuu] = crossResult[2];
						newF2[fffuuu] = crossResult[3];
					}

					BoilerControlSystem cs1 = new BoilerControlSystem(oldF1);
					BoilerControlSystem cs2 = new BoilerControlSystem(oldF2);
					BoilerControlSystem cs3 = new BoilerControlSystem(newF1);
					BoilerControlSystem cs4 = new BoilerControlSystem(newF2);

					BoilerControlSystem tmpCS1 = cs1;
					BoilerControlSystem tmpCS2 = cs2;

					double eff1 = boiler.getEffectivnes(cs1);
					double eff2 = boiler.getEffectivnes(cs2);
					double eff3 = boiler.getEffectivnes(cs3);
					double eff4 = boiler.getEffectivnes(cs4);

					if (eff3 < eff1) {
						tmpCS1 = cs3;
						eff1 = eff3;
					} else if (eff3 < eff2) {
						tmpCS2 = cs3;
						eff2 = eff3;
					}

					if (eff1 < eff2) {
						if (eff4 < eff2) {
							tmpCS2 = cs4;
							eff2 = eff4;
						}
					} else {
						if (eff4 < eff1) {
							tmpCS1 = cs4;
							eff1 = eff4;
						}
					}

					field[j][a] = cs1;
					field[j][b] = cs2;

					if (eff1 < eff2) {
						if (eff1 < bestIndividualScore) {
							bestIndividualScore = eff1;
							bestIndividual = cs1;
						}
					} else {
						if (eff2 < bestIndividualScore) {
							bestIndividualScore = eff2;
							bestIndividual = cs2;
						}
					}
				}
			}
			if (((int) (i / 10)) * 10 == i) {
				int qwe = islandsCount * onIsland / 2;
				for (int k = 0; k != qwe; ++k) {
					int a = (int) (Math.random() * islandsCount);
					int b = a;
					while (a != b) {
						b = (int) (Math.random() * islandsCount);
					}
					int c = (int) (Math.random() * onIsland);
					int d = (int) (Math.random() * onIsland);

					AbstractControlSystem tmp = field[a][c];
					field[a][c] = field[b][d];
					field[b][d] = tmp;
				}
			}
			topScores[i] = bestIndividualScore;
			System.out.println("\n\n///////////////////////////");
			System.out.println("step " + i);
			System.out.println("best " + bestIndividualScore);
		}

	}

	public void constatntSamplingBoilerGenetic() throws Exception {
		bestIndividualScore = Double.POSITIVE_INFINITY;

		for (int i = 0; i < maxEvolutionCount; ++i) {
			System.out.println("\nEvolution step:" + i);
			for (int j = 0; j < islandsCount; ++j) {
				for (int k = 0; k < onIsland; ++k) {

					if (mutationProbability > Math.random()) {
						field[j][k].generateControlSystem();
						continue;
					}

					int a = (int) (Math.random() * onIsland);
					int b = a;
					while (b == a) {
						b = (int) (Math.random() * onIsland);
					}

					ConstantSampling[] oldF1 = (ConstantSampling[]) field[j][a]
							.getFunctions();
					ConstantSampling[] oldF2 = (ConstantSampling[]) field[j][b]
							.getFunctions();

					ConstantSampling[] newF1 = new ConstantSampling[oldF1.length];
					ConstantSampling[] newF2 = new ConstantSampling[oldF1.length];

					for (int fffuuu = 0; fffuuu < oldF1.length; ++fffuuu) {
						ConstantSampling[] crossResult = oldF1[fffuuu]
								.crossover(oldF2[fffuuu]);
						newF1[fffuuu] = crossResult[2];
						newF2[fffuuu] = crossResult[3];
					}

					BoilerControlSystem cs1 = new BoilerControlSystem(oldF1);
					BoilerControlSystem cs2 = new BoilerControlSystem(oldF2);
					BoilerControlSystem cs3 = new BoilerControlSystem(newF1);
					BoilerControlSystem cs4 = new BoilerControlSystem(newF2);

					BoilerControlSystem tmpCS1 = cs1;
					BoilerControlSystem tmpCS2 = cs2;

					double eff1 = boiler.getEffectivnes(cs1);
					double eff2 = boiler.getEffectivnes(cs2);
					double eff3 = boiler.getEffectivnes(cs3);
					double eff4 = boiler.getEffectivnes(cs4);

					if (eff3 < eff1) {
						tmpCS1 = cs3;
						eff1 = eff3;
					} else if (eff3 < eff2) {
						tmpCS2 = cs3;
						eff2 = eff3;
					}

					if (eff1 < eff2) {
						if (eff4 < eff2) {
							tmpCS2 = cs4;
							eff2 = eff4;
						}
					} else {
						if (eff4 < eff1) {
							tmpCS1 = cs4;
							eff1 = eff4;
						}
					}

					field[j][a] = cs1;
					field[j][b] = cs2;

					if (eff1 < eff2) {
						if (eff1 < bestIndividualScore) {
							bestIndividualScore = eff1;
							bestIndividual = cs1;
						}
					} else {
						if (eff2 < bestIndividualScore) {
							bestIndividualScore = eff2;
							bestIndividual = cs2;
						}
					}
				}
			}
			if (((int) (i / 10)) * 10 == i) {
				int qwe = islandsCount * onIsland / 2;
				for (int k = 0; k != qwe; ++k) {
					int a = (int) (Math.random() * islandsCount);
					int b = a;
					while (a != b) {
						b = (int) (Math.random() * islandsCount);
					}
					int c = (int) (Math.random() * onIsland);
					int d = (int) (Math.random() * onIsland);

					AbstractControlSystem tmp = field[a][c];
					field[a][c] = field[b][d];
					field[b][d] = tmp;
				}
			}
			topScores[i] = bestIndividualScore;
			System.out.println("\n\n///////////////////////////");
			System.out.println("step " + i);
			System.out.println("best " + bestIndividualScore);
		}
	}

	public void interpolationBoilerGenetic() throws Exception {
		bestIndividualScore = Double.POSITIVE_INFINITY;

		for (int i = 0; i < maxEvolutionCount; ++i) {
			System.out.println("\nEvolution step:" + i);
			for (int j = 0; j < islandsCount; ++j) {
				for (int k = 0; k < onIsland; ++k) {

					if (mutationProbability > Math.random()) {
						field[j][k].generateControlSystem();
						continue;
					}

					int a = (int) (Math.random() * onIsland);
					int b = a;
					while (b == a) {
						b = (int) (Math.random() * onIsland);
					}

					Interpolation[] oldF1 = (Interpolation[]) field[j][a]
							.getFunctions();
					Interpolation[] oldF2 = (Interpolation[]) field[j][b]
							.getFunctions();

					Interpolation[] newF1 = new Interpolation[oldF1.length];
					Interpolation[] newF2 = new Interpolation[oldF1.length];

					for (int fffuuu = 0; fffuuu < oldF1.length; ++fffuuu) {
						Interpolation[] crossResult = oldF1[fffuuu]
								.crossover(oldF2[fffuuu]);
						newF1[fffuuu] = crossResult[2];
						newF2[fffuuu] = crossResult[3];
					}

					BoilerControlSystem cs1 = new BoilerControlSystem(oldF1);
					BoilerControlSystem cs2 = new BoilerControlSystem(oldF2);
					BoilerControlSystem cs3 = new BoilerControlSystem(newF1);
					BoilerControlSystem cs4 = new BoilerControlSystem(newF2);

					BoilerControlSystem tmpCS1 = cs1;
					BoilerControlSystem tmpCS2 = cs2;

					double eff1 = boiler.getEffectivnes(cs1);
					double eff2 = boiler.getEffectivnes(cs2);
					double eff3 = boiler.getEffectivnes(cs3);
					double eff4 = boiler.getEffectivnes(cs4);

					if (eff3 < eff1) {
						tmpCS1 = cs3;
						eff1 = eff3;
					} else if (eff3 < eff2) {
						tmpCS2 = cs3;
						eff2 = eff3;
					}

					if (eff1 < eff2) {
						if (eff4 < eff2) {
							tmpCS2 = cs4;
							eff2 = eff4;
						}
					} else {
						if (eff4 < eff1) {
							tmpCS1 = cs4;
							eff1 = eff4;
						}
					}

					field[j][a] = cs1;
					field[j][b] = cs2;

					if (eff1 < eff2) {
						if (eff1 < bestIndividualScore) {
							bestIndividualScore = eff1;
							bestIndividual = cs1;
						}
					} else {
						if (eff2 < bestIndividualScore) {
							bestIndividualScore = eff2;
							bestIndividual = cs2;
						}
					}
				}
			}
			if (((int) (i / 10)) * 10 == i) {
				int qwe = islandsCount * onIsland / 2;
				for (int k = 0; k != qwe; ++k) {
					int a = (int) (Math.random() * islandsCount);
					int b = a;
					while (a != b) {
						b = (int) (Math.random() * islandsCount);
					}
					int c = (int) (Math.random() * onIsland);
					int d = (int) (Math.random() * onIsland);

					AbstractControlSystem tmp = field[a][c];
					field[a][c] = field[b][d];
					field[b][d] = tmp;
				}
			}
			topScores[i] = bestIndividualScore;
			System.out.println("\n\n///////////////////////////");
			System.out.println("step " + i);
			System.out.println("best " + bestIndividualScore);
		}
	}

}
